package sm.codec;

import java.io.UnsupportedEncodingException;

import org.apache.mina.core.buffer.IoBuffer;

import sm.codec.api.Codec;
import sm.codec.exception.DecodeException;
import sm.codec.exception.EncodeException;
import sm.codec.type.NullValue;
import sm.def.Type;

public class CharacterCodec implements Codec {

	public static final String CHARSET = "UTF-8";

	protected Object decode(IoBuffer buffer, int type) throws DecodeException {
		byte byteType = buffer.get();
		int subType = byteType & 0x0F;
		Object value = null;
		if (subType > 7) {
			int length = subType - 8;
			if (length > 0) {
				if (length == 7) {
					value = Type.getType(type).getNullValue();
				} else {
					if (length == 5) {
						byte byte1 = buffer.get();
						length = byte1 & 0xFF;
					} else if (length == 6) {
						byte byte1 = buffer.get();
						byte byte2 = buffer.get();
						length = (byte1 & 0xFF) * 256 + (byte2 & 0xFF);
						if (length == 65535) {
							int lengthDesc = buffer.getInt();
							int multi = lengthDesc >>> 16;
							int remain = lengthDesc & 0xFFFF;
							length = multi * 65536 + remain;
						}
					}
					byte[] bytes = new byte[length];
					buffer.get(bytes);
					try {
						value = new String(bytes, CHARSET);
					} catch (UnsupportedEncodingException e) {
						throw new DecodeException(e.getMessage(), e, buffer);
					}
				}

			} else {
				value = "";
			}
		} else {
			throw new DecodeException("The string type " + byteType
					+ " is not supported", buffer);
		}

		return value;
	}

	@Override
	public Object decode(IoBuffer buffer) throws DecodeException {
		return decode(buffer, Type.CHARACTER_TYPE);
	}

	protected void encode(IoBuffer buffer, Object value, int type)
			throws EncodeException {
		byte typeByte = (byte) (type << 4);
		if (value == null || value instanceof NullValue) {

			buffer.put((byte) (0x0F | typeByte));
		} else {
			try {
				String stringValue = (String) value;
				byte[] bytes = stringValue.getBytes(CHARSET);
				int length = bytes.length;
				if (length > 0) {
					if (length < 5) {
						typeByte = (byte) (0x08 | typeByte | length);
						buffer.put(typeByte);
						buffer.put(bytes);
					} else if (length < 256) {
						buffer.put((byte) (0x0D | typeByte));
						buffer.put((byte) (0xFF & length));
						buffer.put(bytes);
					} else if (length < 65536) {
						buffer.put((byte) (0x0E | typeByte));
						buffer.put((byte) ((0xFF00 & length) >>> 8));
						buffer.put((byte) (0xFF & length));
						buffer.put(bytes);
					} else {
						buffer.put((byte) (0x0E | typeByte));
						buffer.put((byte) (0xFF));
						buffer.put((byte) (0xFF));
						int multi = length / 65536;
						int remain = length % 65536;
						buffer.putShort((short) multi);
						buffer.putShort((short) remain);
						buffer.put(bytes);
					}
				} else {
					buffer.put((byte) (0x08 | typeByte));
				}
			} catch (UnsupportedEncodingException e) {
				throw new EncodeException(e.getMessage(), e, buffer, value);
			}
		}

	}

	@Override
	public void encode(IoBuffer buffer, Object value) throws EncodeException {
		encode(buffer, value, Type.CHARACTER_TYPE);

	}
}
